package com.qire.antsrouter.utils;

import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.text.TextUtils;

import com.qire.antscore.common.AntsThreadBuilder;
import com.qire.antscore.common.AssertUtils;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadPoolExecutor;

import dalvik.system.DexFile;

/**
 * 类辅助工具：提供APP内包扫描、Class反射赋值\构建、等功能
 */
public class ClassUtils {

    /**
     * 获取App所有源码包路径
     * @param context 上下文
     * @return 返回包含所有源码的包路劲列表
     * @throws NameNotFoundException 不存在的APP包名异常
     */
    private static List<String> getSourcePaths(Context context) throws NameNotFoundException {
        ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
        List<String> sourcePaths = new ArrayList<>();
        sourcePaths.add(applicationInfo.sourceDir);
        //instant run
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            if (null != applicationInfo.splitSourceDirs) {
                sourcePaths.addAll(Arrays.asList(applicationInfo.splitSourceDirs));
            }
        }
        return sourcePaths;
    }

    /**
     * 扫描Application所有的源码包目录找出指定包名下的所有类名。
     * @param context       Application应用实体
     * @param packageName   指定包名
     * @return  指定包名下的所有类的类名集合
     * @throws PackageManager.NameNotFoundException
     * @throws InterruptedException
     */
    public static Set<String> getClassNamesByPackageName(Application context, final String packageName)
            throws PackageManager.NameNotFoundException, InterruptedException {
        final Set<String> classNames = new HashSet<>();
        List<String> paths = getSourcePaths(context);
        //使用同步计数器判断均处理完成
        final CountDownLatch countDownLatch = new CountDownLatch(paths.size());
//        ThreadPoolExecutor threadPoolExecutor = DefaultPoolExecutor.newDefaultPoolExecutor(paths.size());
        ThreadPoolExecutor threadPoolExecutor = AntsThreadBuilder.defaultExecutor();
        for (final String path : paths) {
            threadPoolExecutor.execute(() -> {
                DexFile dexFile = null;
                try {
                    //加载 apk中的dex 并遍历 获得所有包名为 {packageName} 的类
                    dexFile = new DexFile(path);
                    Enumeration<String> dexEntries = dexFile.entries();
                    while (dexEntries.hasMoreElements()) {
                        String className = dexEntries.nextElement();
                        if (!TextUtils.isEmpty(className) && className.startsWith(packageName)) {
                            classNames.add(className);
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (null != dexFile) {
                        try {
                            dexFile.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                    //释放一个
                    countDownLatch.countDown();
                }
            });
        }
        //等待执行完成
        countDownLatch.await();
        return classNames;
    }

    /**
     * 构造一个指定类型实例
     * @param dstClass 指定的类型
     * @param initArgs 构造参数表
     * @param <T> 返回的实体类型
     * @return 返回一个实体，如果构造发生意外，返回的可能为null。
     */
    public static <T> T newInstance(Class<T> dstClass, Object ... initArgs) {
        try {
            if(AssertUtils.isEmpty(initArgs)) {
                return dstClass.getDeclaredConstructor().newInstance();
            } else {
                Class<?>[] parameterTypes = new Class[initArgs.length];
                for (int index = 0; index < parameterTypes.length; index++) {
                    parameterTypes[index] = initArgs[index].getClass();
                }
                Constructor<T> constructor = dstClass.getDeclaredConstructor(parameterTypes);
                return constructor.newInstance(initArgs);
            }
        } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 反射获取 {@code object} 中的 {@code field} 的值
     * @param field 字段
     * @param object 目标对象
     * @param <T> 字段值类型
     * @return 字段值
     */
    public static <T> T getFieldValue(Field field, Object object) {
        T fieldValue = null;
        try {
            boolean oldAccessible = field.isAccessible();
            if(!oldAccessible) field.setAccessible(true);

            fieldValue = (T) field.get(object);

            if(!oldAccessible) field.setAccessible(false);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return fieldValue;
    }

    /**
     * 反射赋值设置 {@code object} 中的 {@code field} 的值为 {@code fieldValue}
     * @param field 字段
     * @param object 目标对象
     * @param fieldValue 字段值
     * @return 是否设置成功， true 为赋值设置成功， false 为赋值设置失败，可能由于字段无法访问或类型无法转换引起
     */
    public static boolean setFieldValue(Field field, Object object, Object fieldValue) {
        try {
            boolean oldAccessible = field.isAccessible();
            if(!oldAccessible) field.setAccessible(true);

            field.set(object, fieldValue);

            if(!oldAccessible) field.setAccessible(false);

            return true;
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return false;
    }

}
